#include <iostream>
#include <cstring>
#include "analizer.h"
#include "UDPDatagram.h"
#include "Session.h"

using namespace std;

#define BUFFER_SIZE	128*1024

Analizer::Analizer(const char *host, unsigned short port, int id,
		size_t ipSize, size_t tcpSize,
		const std::vector< std::pair<std::string, RootRule *> > &anomalies) :
	_stopping(false), _running(false), _ipSize(ipSize), _tcpSize(tcpSize),
	_num(id), _host(host), _port(port), _anomalies(&anomalies)
{
	_buf = new PacketBuffer(BUFFER_SIZE);
}

Analizer::~Analizer()
{
	stop();
	wait();
	delete _buf;
}

void Analizer::push(Session *ses, const uint8_t *data, size_t length,
						size_t tcpOffset, bool closing)
{
	size_t ipLength = tcpOffset;
	size_t tcpLength = length - tcpOffset;

	if (ipLength > _ipSize)
		ipLength = _ipSize;
	if (tcpLength > _tcpSize)
		tcpLength = _tcpSize;

	Packet packet(ses, data, ipLength, data + tcpOffset, tcpLength, closing);

	if(_buf->push(packet))
		cerr << "Analizer " << _num << ": packet buffer full, dropping packet." << endl;
}

void *Analizer::threadWrapper(void *object)
{
	int ret;
	Analizer *anal = (Analizer *)object;
	ret = anal->run();
	return (void *)ret;
}

static ostream &operator<<(std::ostream &o, const Session &s)
{
	int i;

	i = 3;
	while(1) {
		o << ((s.getSrcAddr() >> i*8) & 0xff);
		if (i == 0)
			break;
		cout << ".";
		--i;
	}

	cout << ":" << s.getSrcPort() << " -> ";

	i = 3;
	while(1) {
		o << ((s.getDstAddr() >> i*8) & 0xff);
		if (i == 0)
			break;
		cout << ".";
		--i;
	}

	cout << ":" << s.getDstPort();

	return o;
}

int Analizer::run()
{
	uint8_t *data = new uint8_t[_ipSize + _tcpSize];
	Packet packet(0, data, _ipSize, data + _ipSize, _tcpSize, false);
	UDPDatagram dgram(_host, _port);
	unsigned int count = 0;
	char buf[256];

	while (!_stopping) {
		if(_buf->pop(packet))
			break;

		Session *ses = packet.getSession();
#ifdef DEBUG
		cout << "Analizer " << _num << " processing packet " << count << endl;
		cout << *ses << endl;
#endif
		int i = 0;
		std::vector< std::pair<std::string, RootRule *> >::const_iterator it;
		for (it = _anomalies->begin(); it != _anomalies->end(); ++it, ++i) {
#ifdef DEBUG
			cout << "CHECK!" << it->first << endl;
#endif
			if (ses->priv) {
				if (!it->second->check((unsigned long *)ses->priv,
					(unsigned long *)packet.getIpData(),
					(unsigned long *)(ses->priv + _ipSize),
					(unsigned long *)packet.getTcpData()))
					continue;
			} else {
				if (!it->second->check(0,
					(unsigned long *)packet.getIpData(),
					0,
					(unsigned long *)packet.getTcpData()))
					continue;
			}

			snprintf(buf, sizeof(buf),
				"%08x:%04x -> %08x:%04x (%08x): %s\n",
				ses->getSrcAddr(), ses->getSrcPort(),
				ses->getDstAddr(), ses->getDstPort(),
				ses->getCounter(), it->first.c_str());
			if (dgram.sendData(buf))
				cerr << "Analizer " << _num << ": could not report packet " << count << endl;
#ifdef DEBUG
			cout << "PASSED!" << endl;
#endif
		}

		if (packet.isClosing()) {
			delete[] ses->priv;
			delete ses;
			goto skip;
		}

		if (!ses->priv) {
			ses->priv = new (nothrow) uint8_t[_ipSize + _tcpSize];
			if (!ses->priv) {
				cerr << "Analizer " << _num
					<< ": session buffer allocation failed, dropping packet history."
					<< endl;
				goto skip;
			}
		}

		memcpy(ses->priv, packet.getIpData(), _ipSize);
		memcpy(ses->priv + _ipSize, packet.getTcpData(), _tcpSize);
		ses->incrCounter();

skip:
		++count;
	}

	delete[] data;

	return 0;
}

void Analizer::start()
{
	_stopping = false;

	if (_running)
		return;

	pthread_create(&_thread, NULL, Analizer::threadWrapper, this);
}

void Analizer::stop()
{
	if (!_running)
		return;

	_stopping = true;
}

int Analizer::wait()
{
	void *ret;

	if (!_running)
		return -1;

	pthread_join(_thread, &ret);

	return (long)ret;
}
